Composite Components
父子组件通讯方式
tip
- 父想把信息传递给子 => 基于属性! !
- 子想改父的数据 => 父把修改自己数据的“方法”,基于“属性”传递给儿子,子执行方法即可
- 父想把一些 HTML 结构传递给子 => 基于属性中的 children [插槽]
- 父再调用子的时候,可以给子设置 ref,以此获取子的实例 (或者子中暴露的数据和方法,使用 useImperativeHandle)
class component 示例
import React from "react"
import PropTypes from "prop-types"
class VoteMain extends React.Component {
// 设置默认值
static defaultProps = {
supportNum: 0,
disapprovalNum: 0,
}
// 校验
static propTypes = {
supportNum: PropTypes.number.isRequired,
disapprovalNum: PropTypes.number.isRequired,
}
render() {
let { supportNum, disapprovalNum } = this.props
let total = supportNum + disapprovalNum
let ratio = total === 0 ? 0 : ((supportNum / total) * 100).toFixed(2)
return (
<div>
<div>支持数:{supportNum}</div>
<div>反对数:{disapprovalNum}</div>
<div>支持比例:{ratio}%</div>
</div>
)
}
}
/**
* 这里需要使用PureComponent
* 因为方法传递进来,子组件没有必要重新渲染
* 因为父组件使用的类组件,那方法也是this中的,永远是同一个方法
*/
class VoteFooter extends React.PureComponent {
// 默认值
static defaultProps = {
support: () => {},
disapprove: () => {},
}
// 校验
static propTypes = {
support: PropTypes.func.isRequired,
disapprove: PropTypes.func.isRequired,
}
componentDidUpdate() {
// 使用了PureComponent这句话不会打印
console.log("footer component updated")
}
render() {
let { support, disapprove } = this.props
return (
<div>
{/* bind也可以,而且还可以传递一些额外值 */}
<button onClick={support.bind(this)}>支持</button>
{/* 子组件调用父组件的方法 */}
<button onClick={disapprove}>反对</button>
</div>
)
}
}
class Vote extends React.Component {
state = {
supportNum: 1,
disapprovalNum: 2,
}
support = () => {
this.setState({
supportNum: this.state.supportNum + 1,
})
}
disapprove = () => {
this.setState({
disapprovalNum: this.state.disapprovalNum + 1,
})
}
render() {
let { supportNum, disapprovalNum } = this.state
return (
<div>
<div>总人数:{supportNum + disapprovalNum}</div>
{/* 父组件向子组件传递参数 */}
<VoteMain supportNum={supportNum} disapprovalNum={disapprovalNum} />
{/* 父组件向子组件传递方法 */}
<VoteFooter support={this.support} disapprove={this.disapprove} />
</div>
)
}
}
export default Vote
functional component 示例
import React, { useCallback, useMemo, useState, memo } from "react"
import PropTypes from "prop-types"
const VoteMain = ({ supportNum, disapprovalNum, title }) => {
// 这里做优化,当且仅当supportNum,和disapprovalNum变化才重新计算
const ratio = useMemo(() => {
let total = supportNum + disapprovalNum
let ratio = total === 0 ? 0 : ((supportNum / total) * 100).toFixed(2)
return ratio
}, [supportNum, disapprovalNum])
return (
<div>
<div>支持数:{supportNum}</div>
<div>反对数:{disapprovalNum}</div>
<div>支持比例:{ratio}%</div>
<div>{title}</div>
</div>
)
}
// 设置默认值
VoteMain.defaultProps = {
supportNum: 0,
disapprovalNum: 0,
}
// 校验
VoteMain.propTypes = {
supportNum: PropTypes.number.isRequired,
disapprovalNum: PropTypes.number.isRequired,
}
const VoteFooter = memo(({ support, disapprove }) => {
console.log("VoteFooter render")
return (
<div>
<button onClick={support}>支持</button>
{/* 子组件调用父组件的方法 */}
<button onClick={disapprove}>反对</button>
</div>
)
})
// 默认值
VoteFooter.defaultProps = {
support: () => {},
disapprove: () => {},
}
// 校验
VoteFooter.propTypes = {
support: PropTypes.func.isRequired,
disapprove: PropTypes.func.isRequired,
}
const Vote = () => {
const [supportNum, setSupportNum] = useState(1)
const [disapprovalNum, setDisapprovalNum] = useState(1)
// 使用useCallback,让函数指向地址不变
// 这里如果设置deps=[]的话,函数永远都是第一次的创建的函数
// 这个函数依赖的上下文的数值永远是初始化的supportNum
// 无论如何点击都不会有效果
const support = useCallback(() => {
setSupportNum(supportNum + 1)
}, [supportNum])
//使用useCallback,让函数指向地址不变
const disapprove = useCallback(() => {
setDisapprovalNum(disapprovalNum + 1)
}, [disapprovalNum])
return (
<div>
<div>总人数:{supportNum + disapprovalNum}</div>
{/* 父组件向子组件传递参数 */}
<VoteMain
title={"第一届投票"}
supportNum={supportNum}
disapprovalNum={disapprovalNum}
/>
{/* 父组件向子组件传递方法 */}
<VoteFooter support={support} disapprove={disapprove} />
</div>
)
}
export default Vote